home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- url.c
-
- This module handles parsing and opening URLs.
-
- Copyright © 1994, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <string.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <Processes.h>
-
- #include "glob.h"
- #include "url.h"
- #include "article.h"
- #include "strutil.h"
- #include "dialog.h"
- #include "message.h"
- #include "apputil.h"
- #include "fileutil.h"
- #include "newswatcher.h"
- #include "memutil.h"
- #include "resutil.h"
- #include "sfutil.h"
- #include "wind.h"
- #include "full.h"
- #include "subject.h"
- #include "ic.h"
-
-
-
- #define isurlschemechar(c) (isalnum((c)) || c == '+' || c == '.' || c == '-')
-
- #define kOptionClickDlg 162
- #define kCantFindHelperDlgID 153
-
- #define kFetchCreatorType 'FTCh'
- #define kFetchMinVersionNumber 0x02120000
- #define kFetchMinVersionNumberStr "2.1.2"
-
- #define kAnarchieCreatorType 'Arch'
- #define kAnarchieMinVersionNumber 0x01200000
- #define kAnarchieMinVersionNumberStr "1.2.0"
- #define kAnarchieMinVersionNumberGURL 0x01402003
-
- #define kFTPHelperURLFileType 'AURL'
-
- #define kNetscapeCreatorType 'MOSS'
- #define kNetscapeMinVersionNumber 0x00096003
- #define kNetscapeMinVersionNumberStr "0.0.9b3"
-
- #define kMacWebCreatorType 'MWEB'
- #define kMacWebMinVersionNumber 0x00000000
- #define kMacWebMinVersionNumberStr ""
-
- #define kMacWAISCreatorType 'MWAS'
- #define kMacWAISMinVersionNumber 0x01298000
- #define kMacWAISMinVersionNumberStr "1.2.9"
-
- #define kMacWebAndWAISOpenURLEventClass 'wwwc'
- #define kMacWebAndWAISOpenURLEventID 'ourl'
- #define kMacWebAndWAISOpenURLKeyword 'kURL'
-
- #define kNCSAMosaicCreatorType 'MOS!'
- #define kNCSAMosaicMinVersionNumber 0x02004008
- #define kNCSAMosaicMinVersionNumberStr "2.0a8"
- #define kNCSAMosaicOpenURLEventClass 'mos!'
- #define kNCSAMosaicOpenURLEventID 'ourl'
-
- #define kTurboGopherCreatorType 'TGOF'
- #define kTurboGopherMinVersionNumber 0x02004001
- #define kTurboGopherMinVersionNumberStr "2.0a1"
- #define kTurboGopherMinVersionNumberCanon 0x02006001
-
- #define kNCSATelnetCreatorType 'NCSA'
- #define kNCSATelnetSettingsFileType 'CONF'
- #define kNCSATelnetMinVersionNumber 0x02400000
- #define kNCSATelnetMinVersionNumberStr "2.4"
-
- #define ktn3270CreatorType 'GFTM'
- #define ktn3270SettingsFileType 'GFTS'
- #define ktn3270MinVersionNumber 0x02402007
- #define ktn3270MinVersionNumberStr "2.4d7"
-
- #define kFingerCreatorType 'PnLF'
- #define kFingerMinVersionNumber 0x01500000
- #define kFingerMinVersionNumberStr "1.5.0"
-
-
-
- typedef char *TURLSchemeName;
-
- static TURLSchemeName gURLSchemeNames[] = {
- ":", /* kNotURL */
- "mailto:",
- "news:",
- "nntp:",
- "ftp:",
- "http:",
- "gopher:",
- "wais:",
- "telnet:",
- "tn3270:",
- "finger:",
- "whois:",
- nil
- };
-
- static TURLHelperInfoPtr gURLInfo[] = {
- nil, /* kNotURL */
- nil, /* kMailtoURL */
- nil, /* kNewsURL */
- nil, /* kNntpURL */
- &gPrefs.ftpHelperInfo,
- &gPrefs.httpHelperInfo,
- &gPrefs.gopherHelperInfo,
- &gPrefs.waisHelperInfo,
- &gPrefs.telnetHelperInfo,
- &gPrefs.tn3270HelperInfo,
- &gPrefs.fingerHelperInfo,
- &gPrefs.whoisHelperInfo
- };
-
- typedef struct THelperInfo {
- OSType sig; /* signature of helper program */
- unsigned long minVersionNumber; /* min version number of helper program */
- char *minVersionStr; /* min version number as a string */
- } THelperInfo;
-
- static THelperInfo gHelperInfo[] = {
- {kAnarchieCreatorType, kAnarchieMinVersionNumber,
- kAnarchieMinVersionNumberStr},
- {kFetchCreatorType, kFetchMinVersionNumber,
- kFetchMinVersionNumberStr},
- {kNetscapeCreatorType, kNetscapeMinVersionNumber,
- kNetscapeMinVersionNumberStr},
- {kMacWebCreatorType, kMacWebMinVersionNumber,
- kMacWebMinVersionNumberStr},
- {kMacWAISCreatorType, kMacWAISMinVersionNumber,
- kMacWAISMinVersionNumberStr},
- {kNCSAMosaicCreatorType, kNCSAMosaicMinVersionNumber,
- kNCSAMosaicMinVersionNumberStr},
- {kTurboGopherCreatorType, kTurboGopherMinVersionNumber,
- kTurboGopherMinVersionNumberStr},
- {kNCSATelnetCreatorType, kNCSATelnetMinVersionNumber,
- kNCSATelnetMinVersionNumberStr},
- {ktn3270CreatorType, ktn3270MinVersionNumber,
- ktn3270MinVersionNumberStr},
- {kFingerCreatorType, kFingerMinVersionNumber,
- kFingerMinVersionNumberStr},
- {0, 0, nil}
- };
-
- typedef OSType TDefaultHelperInfo[5];
-
- static TDefaultHelperInfo gDefaultHelperInfo[] = {
- /* kNotURL */ {0, 0, 0, 0, 0},
- /* kEmailtoURL */ {0, 0, 0, 0, 0},
- /* kNewsURL */ {0, 0, 0, 0, 0},
- /* kNntpURL */ {0, 0, 0, 0, 0},
- /* kFtpURL */ {kFetchCreatorType, kAnarchieCreatorType, 0, 0, 0},
- /* kHttpURL */ {kMacWebCreatorType, kNCSAMosaicCreatorType, kNetscapeCreatorType,
- 0, 0},
- /* kGopherURL */ {kTurboGopherCreatorType, kMacWebCreatorType, kNCSAMosaicCreatorType,
- kNetscapeCreatorType, 0},
- /* kWaisURL */ {kMacWAISCreatorType, kNCSAMosaicCreatorType, 0, 0, 0},
- /* kTelnetURL */ {kNCSATelnetCreatorType, 0, 0, 0, 0},
- /* ktn3270URL */ {ktn3270CreatorType, 0, 0, 0, 0},
- /* kFingerURL */ {kFingerCreatorType, 0, 0, 0, 0},
- /* kWhoisURL */ {kFingerCreatorType, 0, 0, 0, 0}
- };
-
-
-
- /*----------------------------------------------------------------------------
- FindOneURLHelper
-
- Search for a URL helper program.
-
- Entry: sig = signature of helper.
- minVersion = minimum version number of helper, or 0 if none.
-
- Exit: function result = error code.
- *helperInfo = helper info. If no helper is found, the sig is
- set to 0.
- ----------------------------------------------------------------------------*/
-
- static OSErr FindOneURLHelper (OSType sig, unsigned long minVersion,
- TURLHelperInfoPtr helperInfo)
- {
- OSErr err = noErr;
- FSSpec fSpec;
- unsigned long versionNumber = 0;
-
- err = FindAppFromSig(sig, &fSpec, nil, nil);
- if (err != noErr) goto exit;
- if (minVersion != 0) {
- err = GetAppVersionNumber(&fSpec, &versionNumber);
- if (err != noErr) goto exit;
- if (versionNumber < minVersion) goto exit;
- }
- helperInfo->sig = sig;
- helperInfo->versionNumber = versionNumber;
- err = GetLastModDateTime(&fSpec, &helperInfo->lastMod);
- if (err != noErr) goto exit;
-
- return noErr;
-
- exit:
-
- helperInfo->sig = 0;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FindURLHelper
-
- Search for an appropriate default URL helper, if one hasn't been
- found already.
-
- Entry: urlKind = kind of url.
-
- Exit: function result = true if helper found.
- gPrefs fields for helper set.
- ----------------------------------------------------------------------------*/
-
- Boolean FindURLHelper (TURLKind urlKind)
- {
- OSErr err = noErr;
- TURLHelperInfoPtr prefsInfo;
- OSType *defaultHelpers;
- OSType sig;
- short i, j;
- Boolean foundHelper = false;
-
- prefsInfo = gURLInfo[urlKind];
- if (prefsInfo->sig == 0) {
- defaultHelpers = gDefaultHelperInfo[urlKind];
- for (i = 0; ; i++) {
- sig = defaultHelpers[i];
- if (sig == 0) break;
- for (j = 0; ; j++) {
- if (sig == gHelperInfo[j].sig) break;
- }
- err = FindOneURLHelper(sig, gHelperInfo[j].minVersionNumber, prefsInfo);
- if (err == noErr && prefsInfo->sig != 0){
- foundHelper = true;
- break;
- }
- }
- } else {
- foundHelper = true;
- }
- return foundHelper;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ValidURLHelper
-
- Check a file to see if it is a valid URL helper program.
-
- Entry: urlKind = kind of URL.
- fSpec = pointer to application file spec.
-
- Exit: function result = true if valid.
- *helperInfo = helper info.
-
- This function is called when the user selects a new URL helper in the
- preferences dialog. It checks minimum version numbers for the known
- URL helper programs. If the helper is valid, the helper info is filled
- in.
-
- This function is also recalled whenever the user runs a URL helper
- and the last mod date/time of the helper as recorded in the helper info
- does not match the last mod date/time of the file being executed.
- ----------------------------------------------------------------------------*/
-
- Boolean ValidURLHelper (FSSpec *fSpec, TURLHelperInfoPtr helperInfo)
- {
- OSErr err = noErr;
- FInfo fInfo;
- OSType sig, sig2;
- unsigned long versionNumber = 0;
- unsigned long minVersionNumber = 0;
- unsigned long lastMod;
- short j;
- CStr255 fmt, msg;
-
- err = FSpGetFInfo(fSpec, &fInfo);
- if (err != noErr) goto exit;
- sig = fInfo.fdCreator;
-
- for (j = 0; ; j++) {
- sig2 = gHelperInfo[j].sig;
- if (sig2 == 0 || sig == sig2) break;
- }
- if (sig2 != 0) minVersionNumber = gHelperInfo[j].minVersionNumber;
-
- if (minVersionNumber != 0) {
- err = GetAppVersionNumber(fSpec, &versionNumber);
- if (err != noErr) goto exit;
- if (versionNumber < minVersionNumber) {
- GetCString(kStrHelperTooOld, fmt);
- p2cstr(fSpec->name);
- sprintf(msg, fmt, fSpec->name, gHelperInfo[j].minVersionStr);
- c2pstr((char*)fSpec->name);
- StopAlertMessage(msg);
- return false;
- }
- }
-
- err = GetLastModDateTime(fSpec, &lastMod);
- if (err != noErr) goto exit;
-
- helperInfo->sig = sig;
- helperInfo->versionNumber = versionNumber;
- helperInfo->lastMod = lastMod;
-
- return true;
-
- exit:
-
- GetCString(kStrUnexpectedErr, fmt);
- sprintf(msg, fmt, err);
- StopAlertMessage(msg);
- return false;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoCantFindHelperDialog
-
- Present a "can't find helper program" dialog.
-
- Entry: msg = error message.
- ----------------------------------------------------------------------------*/
-
- void DoCantFindHelperDialog (char *msg)
- {
- DialogPtr dlg;
- short item;
- OSErr err = noErr;
-
- c2pstr(msg);
- ParamText((StringPtr)msg, "\p", "\p", "\p");
- p2cstr((StringPtr)msg);
- err = MyGetNewDialog(kCantFindHelperDlgID, ok, 0, &dlg);
- SysBeep(0);
- if (err != noErr) return;
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- DoClose(dlg);
- }
-
-
-
- /*----------------------------------------------------------------------------
- HelperErrorMessage
-
- Issue a helper program error message.
-
- Entry: err = error number.
- sig = helper program signature.
-
- Exit: function result = userCanceledErr.
- ----------------------------------------------------------------------------*/
-
- static OSErr HelperErrorMessage (OSErr err, OSType sig)
- {
- CStr255 fmt, msg;
- Str31 name;
-
- *name = 0;
- FindAppNameFromSig(sig, name);
- p2cstr(name);
- if (err == fnfErr) {
- GetCString(kStrURLHelperNotFound, fmt);
- sprintf(msg, fmt, name);
- DoCantFindHelperDialog(msg);
- } else if (err == memFullErr || err == memFragErr || err == appMemFullErr) {
- GetCString(kStrURLHelperNoMem, fmt);
- sprintf(msg, fmt, name);
- ErrorMessage(msg);
- } else {
- GetCString(kStrURLHelperUnexpectedErr, fmt);
- sprintf(msg, fmt, err, name);
- ErrorMessage(msg);
- }
- return userCanceledErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsSlackMailtoURL
-
- Check for a slack emailto URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = true if slack emailto URL.
- url = parsed URL.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsSlackMailtoURL (Handle text, short begin, short end, CStr255 url)
- {
- char *p, *q, *x, *y, *textEnd;
- long len;
- Boolean couldBeEmailAddress = false;
-
- p = *text + begin;
- q = *text + end;
- textEnd = *text + MyGetHandleSize(text);
-
- if (*p == '<') {
- x = p - 1;
- while (x > *text && *x != CR) x--;
- if (x > *text) x++;
- if (MyStrNEqual(x, "From", 4)) {
- y = x+4;
- } else if (MyStrNEqual(x, "Reply-To", 8)) {
- y = x+8;
- } else {
- y = nil;
- }
- if (y != nil) {
- while (isLWSP(*y) && y < p) y++;
- if (y < p && *y == ':') couldBeEmailAddress = true;
- } else {
- y = q + 1;
- while (y < textEnd && isLWSP(*y)) y++;
- if (y + 6 <= textEnd && MyStrNEqual(y, "wrote:", 6))
- couldBeEmailAddress = true;
- }
- if (!couldBeEmailAddress) return false;
- }
-
- x = p;
- while (*x != '@' && x < q) x++;
- if (x >= q) return false;
- while (*x != '.' && x < q) x++;
- if (x >= q) return false;
-
- if (*p == '<') {
- p++;
- q--;
- }
- len = q - p + 1;
- if (len == 0 || len > 255) return false;
- BlockMoveData(p, url, len);
- url[len] = 0;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsSlackNewsURL
-
- Check for a slack news URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = true if slack news URL.
- url = parsed URL.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsSlackNewsURL (Handle text, short begin, short end, CStr255 url)
- {
- char *p, *q, *x;
- long len;
-
- p = *text + begin;
- q = *text + end;
-
- x = p;
- if (*x != '<') return false;
- while (*x != '@' && x < q) x++;
- if (x >= q) return false;
- while (*x != '.' && x < q) x++;
- if (x >= q) return false;
-
- len = q - p + 1;
- if (len == 0 || len > 255) return false;
- BlockMoveData(p, url, len);
- url[len] = 0;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ParseURL
-
- Parse a URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = kind of URL.
- url = parsed URL.
- *urlBegin = offset in text of beginning of parsed URL.
- *urlEnd = offset in text of end of parsed URL.
-
- If begin == end, ParseURL tries to locate the beginning and end of
- the URL.
- ----------------------------------------------------------------------------*/
-
- static TURLKind ParseURL (Handle text, short begin, short end, CStr255 url,
- short *urlBegin, short *urlEnd)
- {
- char *textEnd, *p, *q, *x, *y, *a, *b;
- long len;
- TURLKind urlKind;
- Boolean isMultiLineURL = false, mightBeMultiLineURL = false;
- short numCR;
- char *schemeName;
-
- p = *text + begin;
- q = *text + end - 1;
-
- if (begin == end) {
-
- textEnd = *text + MyGetHandleSize(text);
-
- numCR = 0;
- x = p;
- y = q;
- while (true) {
- while (x >= *text && *x != '<' && *x != '>' &&
- *x != '"' && *x != CR) x--;
- if (x < *text || *x == '>' || *x == '"') break;
- if (*x == CR) {
- numCR++;
- if (numCR > 10) break;
- x--;
- } else { /* *x == '<' */
- a = x+1;
- while (a < textEnd && isurlschemechar(*a)) a++;
- mightBeMultiLineURL = a < textEnd && *a == ':';
- break;
- }
- }
- if (mightBeMultiLineURL) {
- numCR = 0;
- while (true) {
- while (y < textEnd && *y != '<' && *y != '>' &&
- *y != '"' && *y != CR) y++;
- if (y >= textEnd || *y == '<' || *y == '"') break;
- if (*y == CR) {
- numCR++;
- if (numCR > 10) break;
- y++;
- } else { /* *y == '>' */
- isMultiLineURL = true;
- break;
- }
- }
- }
-
- if (isMultiLineURL) {
- p = x;
- q = y;
- } else {
- while (p >= *text && !isLWSPorCR(*p) && *p != '<' && *p != '"') p--;
- if (*p != '<') p++;
- while (q < textEnd && !isLWSPorCR(*q) && *q != '>' && *q != '"') q++;
- if (*q != '>') q--;
- }
-
- if (p >= q) return kNotURL;
-
- }
-
- while (p < q && isLWSPorCR(*p)) p++;
- while (p < q && isLWSPorCR(*q)) q--;
-
- if (*p == '<') {
- if (*q != '>') return kNotURL;
- } else {
- if (*q == '>') return kNotURL;
- }
-
- *urlBegin = p - *text;
- *urlEnd = q - *text + 1;
- x = p;
- if (*x == '<') x++;
- y = x;
- while (isurlschemechar(*y) && y < q) y++;
- if (*y == ':') {
- if (MyStrNEqual(x, "url:", 4)) x += 4;
- if (*q == '>') q--;
- len = q - x + 1;
- if (len == 0 || len > 255) return kNotURL;
- BlockMoveData(x, url, len);
- url[len] = 0;
- b = url;
- while (*b != ':' && *b != 0) b++;
- if (*b == ':') {
- for (a = url; a < b; a++) *a = tolower(*a);
- }
- for (urlKind = kNotURL; ; urlKind++) {
- schemeName = gURLSchemeNames[urlKind];
- if (schemeName == nil) return kNotURL;
- if (MyStrNEqual(url, schemeName, strlen(schemeName))) goto exit;
- }
- }
-
- begin = p - *text;
- end = q - *text;
- urlKind = kMailtoURL;
- if (IsSlackMailtoURL(text, begin, end, url)) goto exit;
- urlKind = kNewsURL;
- if (IsSlackNewsURL(text, begin, end, url)) goto exit;
- return kNotURL;
-
- exit:
-
- /* Strip line breaks and white space around line breaks from the url. */
-
- for (p = q = url; *p != 0; p++) {
- if (*p == CR) {
- q--;
- while (q >= url && isLWSP(*q)) q--;
- q++;
- p++;
- while (*p != 0 && isLWSP(*p)) p++;
- p--;
- } else {
- *q++ = *p;
- }
- }
- *q = 0;
- return urlKind;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ParseNntpURL
-
- Parse an nntp URL.
-
- Entry: url = URL string.
-
- Exit: function result = error code (paramErr if syntax error).
- host = news server host address.
- *port = port number.
- newsgroup = newsgroup name.
- *artNumber = article number.
- ----------------------------------------------------------------------------*/
-
- static OSErr ParseNntpURL (char *url, char *host, short *port,
- char *newsgroup, long *artNumber)
- {
- char *p, *q;
-
- p = url + strlen("nntp://");
-
- q = host;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
-
- if (*p == ':') {
- *port = atoi(p+1);
- while (*p != '/' && *p != 0) p++;
- } else {
- *port = kNNTPPort;
- }
-
- if (*p != '/') return paramErr;
- p++;
- q = newsgroup;
- while (*p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
-
- if (*p != '/') return paramErr;
- *artNumber = atol(p+1);
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateAURLFile
-
- Create Fetch or Anarchie AURL file.
-
- Entry: sig = signature of helper.
- url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created AURL file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateAURLFile (OSType sig, char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0, len;
- char cmd[1000];
- long count;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature, sig, kFTPHelperURLFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
- len = strlen(url);
- sprintf(cmd, "FCH %s", url);
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateNCSATelnetSettingsFile
-
- Create an NCSA Telnet settings file.
-
- Entry: url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created settings file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateNCSATelnetSettingsFile (char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0;
- char cmd[1000];
- long count;
- CStr255 hostName;
- short port;
- char *p, *q;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature,
- kNCSATelnetCreatorType, kNCSATelnetSettingsFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
-
- p = url + strlen("telnet://");
- q = p;
- while (*q != '@' && *q != 0) q++;
- if (*q == '@') p = q+1;
- q = hostName;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
- if (*p == ':') {
- port = atoi(p+1);
- } else {
- port = 23;
- }
- sprintf(cmd, "name=\"%s\"\rhost=\"%s\"\rport=\"%d\"\r", url, hostName, port);
-
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateTn3270SettingsFile
-
- Create a tn3270 settings file.
-
- Entry: url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created settings file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateTn3270SettingsFile (char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0;
- char cmd[1000];
- long count;
- CStr255 hostName;
- char *p, *q;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature,
- ktn3270CreatorType, ktn3270SettingsFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
-
- p = url + strlen("tn3270://");
- q = p;
- while (*q != '@' && *q != 0) q++;
- if (*q == '@') p = q+1;
- q = hostName;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
- sprintf(cmd, "host_name=\"%s\"\rwindow_title=\"%s\"\r", hostName, url);
-
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenHelperWithURL
-
- Open a helper program and pass it a URL string.
-
- Entry: helperInfo = pointer to helper info.
- url = URL string.
- foreground = true to run helper in foreground, false to run it
- in the background.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenHelperWithURL (TURLHelperInfo *helperInfo, CStr255 url,
- Boolean foreground)
- {
- OSErr err = noErr;
- ProcessSerialNumber psn;
- FSSpec appSpec, docSpec;
- Boolean running;
- unsigned short launchControlFlags;
- char hackedURL[500];
- unsigned long lastMod;
-
- launchControlFlags = launchContinue | launchNoFileFlags;
- if (!foreground) launchControlFlags |= launchDontSwitch;
-
- err = FindAppFromSig(helperInfo->sig, &appSpec, &running, &psn);
- if (err != noErr) goto exit;
-
- err = GetLastModDateTime(&appSpec, &lastMod);
- if (err != noErr) goto exit;
- if (lastMod != helperInfo->lastMod) {
- if (!ValidURLHelper(&appSpec, helperInfo)) return userCanceledErr;
- }
-
- switch (helperInfo->sig) {
-
- case kFetchCreatorType:
-
- err = CreateAURLFile(helperInfo->sig, url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- break;
-
- case kAnarchieCreatorType:
-
- if (helperInfo->versionNumber >= kAnarchieMinVersionNumberGURL) {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- } else {
- err = CreateAURLFile(helperInfo->sig, url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- }
- break;
-
- case kMacWebCreatorType:
- case kMacWAISCreatorType:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kMacWebAndWAISOpenURLEventClass, kMacWebAndWAISOpenURLEventID,
- kMacWebAndWAISOpenURLKeyword, url, 0, launchControlFlags);
- break;
-
- case kNCSAMosaicCreatorType:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kNCSAMosaicOpenURLEventClass, kNCSAMosaicOpenURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- break;
-
- case kTurboGopherCreatorType:
-
- if (helperInfo->versionNumber < kTurboGopherMinVersionNumberCanon) {
- /* Special case for version 2.0a1 - must add <URL:...> wrapper. */
- if (MyStrNEqual(url, "<URL:", 5)) {
- strcpy(hackedURL, url);
- } else if (MyStrNEqual(url, "URL:", 4)) {
- sprintf(hackedURL, "<%s>", url);
- } else {
- sprintf(hackedURL, "<URL:%s>", url);
- }
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, hackedURL, 0, launchControlFlags);
- } else {
- /* Versions >= 2.0b1 accept canoncial form without wrapper. */
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- }
- break;
-
- case kNCSATelnetCreatorType:
-
- err = CreateNCSATelnetSettingsFile(url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- break;
-
- case ktn3270CreatorType:
-
- err = CreateTn3270SettingsFile(url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- break;
-
- default:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- break;
-
- }
-
- if (err != noErr) goto exit;
-
- return noErr;
-
- exit:
-
- return HelperErrorMessage(err, helperInfo->sig);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FlashObject
-
- Flash an object in an article, message, or text window.
-
- Entry: theTE = handle to TextEdit record.
- start = starting position of object.
- end = ending position of object.
- ----------------------------------------------------------------------------*/
-
- static void FlashObject (TEHandle theTE, short start, short end)
- {
- short i;
- long finalTick;
-
- TESetSelect(start, end, theTE);
- for (i = 0; i < 2; i++) {
- Delay(5, &finalTick);
- TEDeactivate(theTE);
- Delay(5, &finalTick);
- TEActivate(theTE);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- ProcessURLEscapeCodes
-
- Process "%xx" escape codes in a URL string (replace them by the characters
- they represent).
-
- Entry: url = URL with escape codes.
-
- Exit: url = URL with escape codes replaced by the characters they
- represent.
- ----------------------------------------------------------------------------*/
-
- static void ProcessURLEscapeCodes (char *url)
- {
- char *p, *q;
- char c1, c2;
-
- p = q = url;
- while (*p != 0) {
- if (*p == '%') {
- c1 = tolower(*(p+1));
- c2 = tolower(*(p+2));
- if (isxdigit(c1) && isxdigit(c2)) {
- c1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
- c2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
- *q++ = (c1 << 4) + c2;
- p += 3;
- } else {
- *q++ = *p++;
- }
- } else {
- *q++ = *p++;
- }
- }
- *q = 0;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenNewsURL
-
- Open a news URL or slack URL.
-
- Entry: url = news URL or slack URL.
-
- Exit: function result = error code (fnfErr if article or group not found).
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenNewsURL (char *url)
- {
- char *p;
- Cell theCell;
- Boolean hasArts;
-
- if (MyStrNEqual(url, "news:", 5)) {
- if (url[5] == '*' && url[6] == 0) {
- /* Show full group list window */
- if (((WindowPeek)gFullGroupWindow)->visible) {
- MySelectWindow(gFullGroupWindow);
- return noErr;
- } else {
- return DoShowHideFullGroupList();
- }
- } else {
- for (p = url; *p != 0; p++)
- if (*p == '@')
- return OpenReferencedArticle(url);
- /* Open group from full group list */
- theCell.h = 0;
- theCell.v = FindGroupIndex(url+5);
- if (theCell.v == -1) return fnfErr;
- return OpenGroupCell(gFullGroupWindow, theCell, gPrefs.maxFetch, &hasArts);
- }
- } else {
- return OpenReferencedArticle(url);
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenURL
-
- Open a URL.
-
- Entry: wind = pointer to article, message, or text window.
- theTE = handle to text edit record.
- begin = offset in text of beginning of url.
- end = offset in text of end of url.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenURL (WindowPtr wind, TEHandle theTE, short begin, short end)
- {
- TWindow **info;
- CStr255 url, msg;
- short urlBegin, urlEnd;
- TURLKind urlKind;
- TURLHelperInfoPtr helperInfo;
- Boolean foreground;
- CStr255 host, newsgroup;
- short port;
- long artNumber;
- OSErr err = noErr;
-
- info = (TWindow**)GetWRefCon(wind);
- urlKind = ParseURL((**theTE).hText, begin, end, url, &urlBegin, &urlEnd);
- if (urlKind == kNotURL) {
- SysBeep(0);
- return userCanceledErr;
- }
- FlashObject(theTE, urlBegin, urlEnd);
-
- switch (urlKind) {
-
- case kMailtoURL:
-
- ProcessURLEscapeCodes(url);
- return OpenMailWindow(url);
-
- case kNewsURL:
-
- ProcessURLEscapeCodes(url);
- err = OpenNewsURL(url);
- if (err == fnfErr) {
- NoteMessageNumber(kStrArticleOrGroupNotFound);
- return userCanceledErr;
- } else {
- return err;
- }
-
- case kNntpURL:
-
- ProcessURLEscapeCodes(url);
- err = ParseNntpURL(url, host, &port, newsgroup, &artNumber);
- if (err == paramErr) {
- ErrorMessageNumber(kStrNntpURLSyntaxError);
- return userCanceledErr;
- } else if (err != noErr) {
- return err;
- }
- err = OpenArticleOnAnyServer(host, port, newsgroup, artNumber);
- if (err == fnfErr) {
- NoteMessageNumber(kStrArticleOrGroupNotFound);
- return userCanceledErr;
- } else {
- return err;
- }
-
- default:
-
- switch (urlKind) {
- case kFtpURL:
- MyICReadSharedPrefs(kICftpHelper);
- break;
- case kHttpURL:
- MyICReadSharedPrefs(kIChttpHelper);
- break;
- case kGopherURL:
- MyICReadSharedPrefs(kICgopherHelper);
- break;
- case kWaisURL:
- MyICReadSharedPrefs(kICwaisHelper);
- break;
- case kTelnetURL:
- MyICReadSharedPrefs(kICtelnetHelper);
- break;
- case kTn3270URL:
- MyICReadSharedPrefs(kICtn3270Helper);
- break;
- case kFingerURL:
- MyICReadSharedPrefs(kICfingerHelper);
- break;
- case kWhoisURL:
- MyICReadSharedPrefs(kICwhoisHelper);
- break;
- }
- helperInfo = gURLInfo[urlKind];
- foreground = urlKind != kFtpURL || url[strlen(url)-1] == '/';
- if (FindURLHelper(urlKind)) {
- return OpenHelperWithURL(helperInfo, url, foreground);
- } else {
- GetCString(kStrCantFindDefaultURLHelper, msg);
- DoCantFindHelperDialog(msg);
- return userCanceledErr;
- }
-
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- CommandClick
-
- Handle a command-click in an article, message, or text window.
-
- Entry: wind = pointer to article, message, or text window.
- theTE = handle to TextEdit record in which command-click occurred.
- oldSelStart = start of selection range before user command-clicked.
- oldSelEnd = end of selection range before user command-clicked.
- modifiers = modifiers field from event record.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr CommandClick (WindowPtr wind, TEHandle theTE,
- short oldSelStart, short oldSelEnd, short modifiers)
- {
- TWindow **info;
- short selStart, selEnd, begin, end;
- DialogPtr dlg;
- short item;
- OSErr err = noErr;
-
- if (!gStartupOK) return noErr;
-
- if ((modifiers & optionKey) == 0 && (modifiers & cmdKey) == 0) return noErr;
-
- if ((modifiers & cmdKey) == 0) {
- err = MyGetNewDialog(kOptionClickDlg, ok, cancel, &dlg);
- SysBeep(0);
- if (err != noErr) return err;
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- DoClose(dlg);
- if (item == cancel) return userCanceledErr;
- }
-
- info = (TWindow**)GetWRefCon(wind);
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- if (selStart != selEnd) return noErr;
-
- if (oldSelStart < oldSelEnd && oldSelStart <= selStart &&
- selStart <= oldSelEnd)
- {
- begin = oldSelStart;
- end = oldSelEnd;
- } else {
- begin = end = selStart;
- }
-
- return OpenURL(wind, theTE, begin, end);
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsURL
-
- Check to see if the current selected text in a text edit record is
- a URL.
-
- Entry: theTE = handle to TextEdit record.
-
- Exit: function result = true if current selection is a URL.
- ----------------------------------------------------------------------------*/
-
- Boolean IsURL (TEHandle theTE)
- {
- CStr255 url;
- short urlBegin, urlEnd;
-
- return ParseURL((**theTE).hText, (**theTE).selStart, (**theTE).selEnd,
- url, &urlBegin, &urlEnd) != kNotURL;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoOpenURL
-
- Handle the "Open URL" command.
-
- Entry: wind = pointer to article, message, or text window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoOpenURL (WindowPtr wind)
- {
- TWindow **info;
- TEHandle theTE;
- TMsgFieldInfo **fields;
- short curField;
-
- info = (TWindow**)GetWRefCon(wind);
- if ((**info).kind == kMessage) {
- fields = (**info).fields;
- curField = (**info).curField;
- theTE = (*fields)[curField].edit;
- } else {
- theTE = (**info).theTE;
- }
- return OpenURL(wind, theTE, (**theTE).selStart, (**theTE).selEnd);
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenURLString
-
- Open a news, nntp, or mailto URL.
-
- Entry: urlString = the URL string.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr OpenURLString (char *urlString)
- {
- OSErr err = noErr;
- Handle h = nil;
- short len, urlBegin, urlEnd;
- CStr255 url;
- TURLKind urlKind;
- CStr255 host, newsgroup;
- short port;
- long artNumber;
-
- len = strlen(urlString);
- err = MyPtrToHand(urlString, &h, len);
- if (err != noErr) return err;
- urlKind = ParseURL(h, 0, len, url, &urlBegin, &urlEnd);
- MyDisposeHandle(h);
- ProcessURLEscapeCodes(url);
-
- switch (urlKind) {
-
- case kMailtoURL:
-
- return OpenMailWindow(url);
-
- case kNewsURL:
-
- return OpenNewsURL(url);
-
- case kNntpURL:
-
- err = ParseNntpURL(url, host, &port, newsgroup, &artNumber);
- if (err != noErr) return err;
- return OpenArticleOnAnyServer(host, port, newsgroup, artNumber);
-
- default:
-
- return paramErr;
-
- }
- }
-